package com.summer.socket;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

/**
 * NIO 实现
 * @author Administrator
 *
 */
public class SimpleCase2 {

	public static class NIOService {
	
		// 通道管理
		private Selector selector;
		
		/**
		 * 获得一个ServerSocket通道，并对该通道做一些初始化操作
		 * 
		 * @param port 绑定的端口
		 * @throws IOException
		 */
		public void initService(int port) throws IOException{
			// 获得一个ServerSocket通道
			ServerSocketChannel socketChannel = ServerSocketChannel.open();
			// 设置通道为非阻塞
			socketChannel.configureBlocking(false);
			// 讲通道对应的serverSocket绑定到port端口
			socketChannel.socket().bind(new InetSocketAddress(port));
			// 获得一个通道管理器
			this.selector = Selector.open();
			// 将通道管理器和该通道绑定，并为该通道注册SelectionKey.OP_ACCPET事件，注册该事件后，
			// 当该事件到达时，selector.select()会返回，如果该事件没到达selector.select()会一直阻塞.
			socketChannel.register(selector, SelectionKey.OP_ACCEPT);
		}

		/**
		 * 采用轮训的方式监听selector上是否有需要处理的事件，如果有，则进行处理
		 * @throws IOException
		 */
		public void listen() throws IOException {
			System.out.println("服务端启动成功！");
			// 轮询访问selector
			while(true) {
				// 当注册事件到达时，方法返回；否则，该方法会一直阻塞
				this.selector.select();
//				this.selector.select(1000);//1秒以后返回
//				this.selector.wakeup();//可以唤醒selector
				// 获得selector中选中的项的迭代器，选中的项为注册的事件
				Iterator<SelectionKey> ite = this.selector.selectedKeys().iterator();
				while(ite.hasNext()){
					SelectionKey key = ite.next();
					// 删除已选的key，以防重复处理
					ite.remove();
					
					handler(key);
				}
			}
		}
		
		/**
		 * 处理请求
		 * 
		 * @param key
		 * @throws IOException
		 */
		public void handler(SelectionKey key) throws IOException {
			if(key.isAcceptable()){
				// 客户端请求连接事件
				handlerAccept(key);
			} else if(key.isReadable()) {
				// 获得了可读的事件
				handelerRead(key);
			}
		}
		
		/**
		 * 处理连接请求
		 * 
		 * @param key
		 * @throws IOException
		 */
		public void handlerAccept(SelectionKey key) throws IOException {
			ServerSocketChannel server = (ServerSocketChannel) key.channel();
			// 获得和客户端连接的通道
			SocketChannel channel = server.accept();
			// 设置成非阻塞
			channel.configureBlocking(false);

			// 在这里可以给客户端发送信息
			System.out.println("新的客户端连接");
			// 在和客户端连接成功之后，为了可以接受到客户端信息，需要给通道设置读的权限。
			// SelectionKey.OP_WRITE 表示底层缓冲区是否有空间，有则key.isWritable()返回true，
			// 如果不关注这个标记时间则返回false
			channel.register(this.selector, SelectionKey.OP_READ);
		}
		
		/**
		 * 处理读的请求
		 * 
		 * @param key
		 * @throws IOException
		 */
		public void handelerRead(SelectionKey key) throws IOException {
			// 服务器可读取消息：得到世间发生的Socket通道
			SocketChannel channel = (SocketChannel) key.channel();
			// 创建读取的缓冲区
			ByteBuffer buffer = ByteBuffer.allocate(10);
			if(channel.read(buffer) > 0) {
				byte[] data = buffer.array();
				String msg = new String(data).trim();
				System.out.println("服务器端收到信息：" + msg);
				
				// 将消息回送给客户端
				ByteBuffer outBuffer = ByteBuffer.wrap(msg.getBytes());
				channel.write(outBuffer);
			} else {
				System.out.println("客户端关闭");
				key.cancel();
				return;
			}
		}
		
		/**
		 * 启动服务端测试
		 * 
		 * @param args
		 * @throws IOException
		 */
		public static void main(String[] args) throws IOException {
			NIOService server = new NIOService();
			server.initService(9999);
			server.listen();
		}
		
	}
	
}
